package Code.Basic.Entity;

import java.awt.geom.Line2D;

import org.jrabbit.base.graphics.transforms.Vector2d;
import org.jrabbit.standard.game.graphics.skins.animation.AnimatedSkin;
import org.jrabbit.standard.game.graphics.skins.image.ImageSkin;
import org.lwjgl.opengl.GL11;

import Code.Basic.AI.Pathfinding.OpenZone;
import Code.Basic.Entity.Attributes.Armor;
import Code.Basic.World.GameWorld;

public class GenericCharacter extends PhysicsSprite
{
	protected String name;
	protected Armor[] armor;
	protected float health;
	protected float maxHealth;
	
	/* Score for the Character
	 * If the character is a player then it represents the player's score
	 * If the character is an enemy then it represents the number of points awarded when defeated
	 */
	protected int score;
	
	/** 0: Neutral. 1: Friendly. -1: Hostile. */
	protected int factionRatings[];
	/**
	 * What alliance the player has:
	 * 0 - neutral.
	 * 1 - allied
	 * 2 - hostile.
	 */
	protected int faction;
	protected GameWorld gameWorld;
	
	//Pathing members
	protected OpenZone currentZone = null;
	protected Line2D pathLine;
	protected Vector2d lastPoint;
	
	protected float shadowXScale;
	protected float shadowYScale;
	protected float shadowXOffset;
	protected float shadowYOffset;
	protected float shadowDarkness;

	public GenericCharacter(String filepath)
	{
		super(filepath);
		setup();
	}

	public GenericCharacter(AnimatedSkin skin)
	{
		super(skin);
		setup();
	}

	private void setup()
	{
		name = "Character";
		armor = new Armor[] {
								new Armor(0, 0), 	// Melee
								new Armor(0, 0), 	// Bullet
								new Armor(0, 0),	// Explosive
								new Armor(0, 0),	// Fire
								new Armor(0, 0)		// Electric
							};
		health = 10f;
		maxHealth = 10f;
		faction = 0;
		factionRatings = new int[] {0, 0, 0};
		lastPoint = new Vector2d();
		pathLine = new Line2D.Double();

		shadowXScale = 0.5f;
		shadowYScale = 0.5f;
		shadowDarkness = 0.7f;
	}
	
	public void setName(String newName)
	{
		name = newName;
	}
	
	public String getName()
	{
		return name;
	}
	
	public void defineHealth(float newHealth)
	{
		health = maxHealth = newHealth;
	}
	
	public float getHealth()
	{
		return health;
	}
	
	public float getMaximumHealth()
	{
		return maxHealth;
	}
	
	public float getHealthPercentage()
	{
		return health / maxHealth;
	}
	
	public void addHealth(float amount)
	{
		health += amount;
		assureHealth();
	}
	
	public void removeHealth(float amount)
	{
		health -= amount;
		assureHealth();
	}
	
	public void damage(float amount, int type)
	{
		removeHealth(armor[type].reducedDamage(amount));
	}
	
	private void assureHealth()
	{
		if(health <= 0)
		{
			health = 0;
			die();
		}
		if(health > maxHealth)
		{
			health = maxHealth;
		}
	}
	
	public void addScore(int points)
	{
		score += points;
	}
	
	public int getScore()
	{
		return score;
	}
	
	protected void die()
	{
		onDeath();
		getWorld().removeCharacter(this);
	}
	
	protected void onDeath()
	{
		
	}
	
	public boolean isDead()
	{
		return health <= 0;
	}
	
	public void setArmorValues(Armor[] newArmor)
	{
		armor = newArmor;
	}
	
	public Armor[] getArmor()
	{
		return armor;
	}
	
	public Armor getArmorValue(int damageType)
	{
		return armor[damageType];
	}
	
	public boolean isAlive()
	{
		return health > 0;
	}
	
	public boolean isNeutral(GenericCharacter c)
	{
		return factionRatings[c.faction] == 0;
	}
	
	public boolean isFriendly(GenericCharacter c)
	{
		return factionRatings[c.faction] == 1;
	}
	
	public boolean isHostile(GenericCharacter c)
	{
		return factionRatings[c.faction] == -1;
	}
	
	public int getFaction()
	{
		return faction;
	}
	
	public void setFaction(int f)
	{
		faction = f;
	}
	
	public void handleCollision(GenericEffect gE)
	{
		if(gE.targetsAllowed[faction])
		{
			if(gE.intersects(this))
			{
				gE.affect(this);
			}
		}
	}
	
	public void setWorld(GameWorld w)
	{
		gameWorld = w;
	}
	
	public GameWorld getWorld()
	{
		return gameWorld;
	}
	
	/********************************/
	/** start of path finding code **/
	/********************************/
	
	protected void setOpenZone(OpenZone zone)
	{
		currentZone = zone;
	}
	
	public OpenZone getOpenZone()
	{
		return currentZone;
	}
	
	public Line2D getPathLine()
	{
		return pathLine;
	}
	
	public void resetZone()
	{
		if(gameWorld != null)
		{
			setOpenZone(gameWorld.getPathMap().getContainingZone(location));
		}
		else
		{
			currentZone = null;
		}
	}
	
	public void characterEnteredOpenZone(OpenZone zone)
	{
		if (currentZone != null)
		{
			this.characterExitedOpenZone(currentZone);
		}
		currentZone = zone;
		zone.characterEnteredZone(this);
	}
	
	public void characterExitedOpenZone(OpenZone zone)
	{
		zone.characterExitedZone(this);
	}
	
	/******************************/
	/** end of path finding code **/
	/******************************/
	
	//************************************* Overridden Methods ***********
	public void setLocation(Vector2d loc)
	{
		super.setLocation(loc);
		lastPoint.set(loc);
		pathLine = new Line2D.Double(loc.x(), loc.y(), loc.x(), loc.y());
	}
	
	public void setLocation(double x, double y)
	{
		super.setLocation(x, y);
		lastPoint.set(x, y);
		pathLine = new Line2D.Double(x, y, x, y);
	}
	
	public void update(int time)
	{
		pathLine.setLine(lastPoint.x(), lastPoint.y(), location.x(), location.y());
		lastPoint.set(location);
		super.update(time);
	}
	
	public void drawShadow()
	{
		bind();
		Shadow.draw(shadowXOffset, shadowYOffset, shadowXScale, shadowYScale, shadowDarkness);
		release();
	}
	
	//********************************* Shadow Code **********************
	protected static class Shadow
	{
		private static ImageSkin shadow = new ImageSkin("Resources/Images/Characters/Misc/Shadow.png");
		
		static void draw(float x, float y, float w, float h, float darkness)
		{
			GL11.glTranslatef(x, y, 0);
			GL11.glScalef(w, h, 1);
			GL11.glColor4f(0, 0, 0, darkness);
			shadow.render();
		}
	}
}